{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5rmpybwysXGV"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "m8y3rGtQsYP2"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hrXv0rU9sIma"
      },
      "source": [
        "# 基本的なトレーニングループ"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7S0BwJ_8sLu7"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>     <a target=\"_blank\" href=\"https://www.tensorflow.org/guide/basic_training_loops\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org で表示</a>   </td>\n",
        "  <td>     <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/guide/basic_training_loops.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab で実行</a>   </td>\n",
        "  <td>     <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/guide/basic_training_loops.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示</a>   </td>\n",
        "  <td>     <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/guide/basic_training_loops.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード</a>   </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "k2o3TTG4TFpt"
      },
      "source": [
        "以前のガイドでは、[テンソル](./tensor.ipynb)、[変数](./variable.ipynb)、[勾配テープ](autodiff.ipynb)、[モジュール](./intro_to_modules.ipynb)について学習しました。このガイドでは、これらをすべて組み合わせてモデルをトレーニングします。\n",
        "\n",
        "TensorFlow には、[tf.Keras API](keras/overview.ipynb) という、抽象化によってボイラープレートを削減する高度なニューラルネットワーク API も含まれていますが、このガイドでは基本的なクラスを使用します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3LXMVuV0VhDr"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NiolgWMPgpwI"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iKD__8kFCKNt"
      },
      "source": [
        "## 機械学習問題を解決する\n",
        "\n",
        "機械学習問題を解決する場合、一般的には次の手順を実行します。\n",
        "\n",
        "- トレーニングデータを取得する。\n",
        "- モデルを定義する。\n",
        "- 損失関数を定義する。\n",
        "- トレーニングデータを読み込み、理想値から損失を計算する。\n",
        "- その損失の勾配を計算し、*オプティマイザ*を使用してデータに適合するように変数を調整します。\n",
        "- 結果を評価する。\n",
        "\n",
        "説明のため、このガイドでは $W$（重み）と $b$（バイアス）の 2 つの変数を持つ単純な線形モデルである $f(x) = x * W + b$ を開発します。\n",
        "\n",
        "これは最も基本的な機械学習問題です。$x$ と $y$ が与えられている前提で  [単純線形回帰](https://en.wikipedia.org/wiki/Linear_regression#Simple_and_multiple_linear_regression)を使用して直線の傾きとオフセットを求めてみます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qutT_fkl_CBc"
      },
      "source": [
        "## データ\n",
        "\n",
        "教師あり学習では、*入力*（通常は *x* と表記）と*出力*（*y* と表記され、*ラベル*と呼ばれることが多い）を使用します。目標は、入力と出力のペアから学習し、入力から出力の値を予測できるようにすることです。\n",
        "\n",
        "TensorFlow での各データ入力はほぼ必ずテンソルで表現され、多くの場合はベクトルです。教師ありトレーニングの場合は出力（または予測したい値）もテンソルになります。\n",
        "\n",
        "以下は、直線上の点にガウス（正規）ノイズを付加することによって合成されたデータです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "NzivK2ATByOz"
      },
      "outputs": [],
      "source": [
        "# The actual line\n",
        "TRUE_W = 3.0\n",
        "TRUE_B = 2.0\n",
        "\n",
        "NUM_EXAMPLES = 1000\n",
        "\n",
        "# A vector of random x values\n",
        "x = tf.random.normal(shape=[NUM_EXAMPLES])\n",
        "\n",
        "# Generate some noise\n",
        "noise = tf.random.normal(shape=[NUM_EXAMPLES])\n",
        "\n",
        "# Calculate y\n",
        "y = x * TRUE_W + TRUE_B + noise"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IlFd_HVBFGIF"
      },
      "outputs": [],
      "source": [
        "# Plot all the data\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "plt.scatter(x, y, c=\"b\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UH95XUzhL99d"
      },
      "source": [
        "テンソルは一般的に*バッチ*か、入力と出力のグループにまとめられます。バッチにはいくつかのトレーニング上のメリットがあり、アクセラレーターやベクトル化計算でうまく機能します。このデータセットの小ささを考慮すると、データセット全体を単一のバッチとして扱うことができます。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "gFzH64Jn9PIm"
      },
      "source": [
        "## モデルを定義する\n",
        "\n",
        "モデル内のすべての重みを表現するには `tf.Variable` を使用します。`tf.Variable` は値を格納し、必要に応じてこれをテンソル形式で提供します。詳細については、[変数ガイド](./variable.ipynb)を参照してください。\n",
        "\n",
        "変数と計算のカプセル化には `tf.Module` を使用します。任意の Python オブジェクトを使用することもできますが、この方法ではより簡単に保存できます。\n",
        "\n",
        "ここでは *w* と *b* の両方を変数として定義しています。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_WRu7Pze7wk8"
      },
      "outputs": [],
      "source": [
        "class MyModel(tf.Module):\n",
        "  def __init__(self, **kwargs):\n",
        "    super().__init__(**kwargs)\n",
        "    # Initialize the weights to `5.0` and the bias to `0.0`\n",
        "    # In practice, these should be randomly initialized\n",
        "    self.w = tf.Variable(5.0)\n",
        "    self.b = tf.Variable(0.0)\n",
        "\n",
        "  def __call__(self, x):\n",
        "    return self.w * x + self.b\n",
        "\n",
        "model = MyModel()\n",
        "\n",
        "# List the variables tf.modules's built-in variable aggregation.\n",
        "print(\"Variables:\", model.variables)\n",
        "\n",
        "# Verify the model works\n",
        "assert model(3.0).numpy() == 15.0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rdpN_3ssG9D5"
      },
      "source": [
        "ここでは初期変数が固定されていますが、Keras には他の Keras の有無にかかわらず使用できる多くの[初期化子](https://www.tensorflow.org/api_docs/python/tf/keras/initializers)があります。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xa6j_yXa-j79"
      },
      "source": [
        "### 損失関数を定義する\n",
        "\n",
        "損失関数は、特定の入力に対するモデルの出力と目標出力との一致度を評価します。目標は、トレーニング中のこの差を最小限に抑えることです。 「平均二乗」誤差としても知られる標準 L2 損失を定義します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Y0ysUFGY924U"
      },
      "outputs": [],
      "source": [
        "# This computes a single loss value for an entire batch\n",
        "def loss(target_y, predicted_y):\n",
        "  return tf.reduce_mean(tf.square(target_y - predicted_y))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-50nq-wPBsAW"
      },
      "source": [
        "モデルをトレーニングする前に、モデルの予測を赤でプロットし、トレーニングデータを青でプロットすることにより、損失の値を視覚化できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "_eb83LtrB4nt"
      },
      "outputs": [],
      "source": [
        "plt.scatter(x, y, c=\"b\")\n",
        "plt.scatter(x, model(x), c=\"r\")\n",
        "plt.show()\n",
        "\n",
        "print(\"Current loss: %1.6f\" % loss(y, model(x)).numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sSDP-yeq_4jE"
      },
      "source": [
        "### トレーニングループを定義する\n",
        "\n",
        "トレーニングループは、次の 3 つを順番に繰り返し実行するタスクで構成されます。\n",
        "\n",
        "- モデル経由で入力のバッチを送信して出力を生成する\n",
        "- 出力を出力（またはラベル）と比較して損失を計算する\n",
        "- 勾配テープを使用して勾配を検出する\n",
        "- これらの勾配を使用して変数を最適化する\n",
        "\n",
        "この例では、[最急降下法](https://en.wikipedia.org/wiki/Gradient_descent)を使用してモデルをトレーニングできます。\n",
        "\n",
        "`tf.keras.optimizers` でキャプチャされる勾配降下法のスキームには多くのバリエーションがありますが、ここでは基本原理から構築するという姿勢で自動微分を行う `tf.GradientTape` と値を減少させる `tf.assign_sub`（`tf.assign` と `tf.sub` の組み合わせ）を使用して基本的な計算を自分で実装してみましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "MBIACgdnA55X"
      },
      "outputs": [],
      "source": [
        "# Given a callable model, inputs, outputs, and a learning rate...\n",
        "def train(model, x, y, learning_rate):\n",
        "\n",
        "  with tf.GradientTape() as t:\n",
        "    # Trainable variables are automatically tracked by GradientTape\n",
        "    current_loss = loss(y, model(x))\n",
        "\n",
        "  # Use GradientTape to calculate the gradients with respect to W and b\n",
        "  dw, db = t.gradient(current_loss, [model.w, model.b])\n",
        "\n",
        "  # Subtract the gradient scaled by the learning rate\n",
        "  model.w.assign_sub(learning_rate * dw)\n",
        "  model.b.assign_sub(learning_rate * db)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RwWPaJryD2aN"
      },
      "source": [
        "トレーニングを観察するため、トレーニングループを介して *x* と *y* の同じバッチを送信し、`W` と `b` がどのように変化するかを見ることができます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "XdfkR223D9dW"
      },
      "outputs": [],
      "source": [
        "model = MyModel()\n",
        "\n",
        "# Collect the history of W-values and b-values to plot later\n",
        "Ws, bs = [], []\n",
        "epochs = range(10)\n",
        "\n",
        "# Define a training loop\n",
        "def training_loop(model, x, y):\n",
        "\n",
        "  for epoch in epochs:\n",
        "    # Update the model with the single giant batch\n",
        "    train(model, x, y, learning_rate=0.1)\n",
        "\n",
        "    # Track this before I update\n",
        "    Ws.append(model.w.numpy())\n",
        "    bs.append(model.b.numpy())\n",
        "    current_loss = loss(y, model(x))\n",
        "\n",
        "    print(\"Epoch %2d: W=%1.2f b=%1.2f, loss=%2.5f\" %\n",
        "          (epoch, Ws[-1], bs[-1], current_loss))\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "iRuNUghs1lHY"
      },
      "outputs": [],
      "source": [
        "print(\"Starting: W=%1.2f b=%1.2f, loss=%2.5f\" %\n",
        "      (model.w, model.b, loss(y, model(x))))\n",
        "\n",
        "# Do the training\n",
        "training_loop(model, x, y)\n",
        "\n",
        "# Plot it\n",
        "plt.plot(epochs, Ws, \"r\",\n",
        "         epochs, bs, \"b\")\n",
        "\n",
        "plt.plot([TRUE_W] * len(epochs), \"r--\",\n",
        "         [TRUE_B] * len(epochs), \"b--\")\n",
        "\n",
        "plt.legend([\"W\", \"b\", \"True W\", \"True b\"])\n",
        "plt.show()\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tpTEjWWex568"
      },
      "outputs": [],
      "source": [
        "# Visualize how the trained model performs\n",
        "plt.scatter(x, y, c=\"b\")\n",
        "plt.scatter(x, model(x), c=\"r\")\n",
        "plt.show()\n",
        "\n",
        "print(\"Current loss: %1.6f\" % loss(model(x), y).numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DODMMmfLIiOC"
      },
      "source": [
        "## Keras を使用した場合の同じ方法\n",
        "\n",
        "上記のコードを Keras で書いたコードと対比すると参考になります。\n",
        "\n",
        "`tf.keras.Model` をサブクラス化すると、モデルの定義はまったく同じように見えます。Keras モデルは最終的にモジュールから継承するということを覚えておいてください。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Z86hCI0x1YX3"
      },
      "outputs": [],
      "source": [
        "class MyModelKeras(tf.keras.Model):\n",
        "  def __init__(self, **kwargs):\n",
        "    super().__init__(**kwargs)\n",
        "    # Initialize the weights to `5.0` and the bias to `0.0`\n",
        "    # In practice, these should be randomly initialized\n",
        "    self.w = tf.Variable(5.0)\n",
        "    self.b = tf.Variable(0.0)\n",
        "\n",
        "  def __call__(self, x, **kwargs):\n",
        "    return self.w * x + self.b\n",
        "\n",
        "keras_model = MyModelKeras()\n",
        "\n",
        "# Reuse the training loop with a Keras model\n",
        "training_loop(keras_model, x, y)\n",
        "\n",
        "# You can also save a checkpoint using Keras's built-in support\n",
        "keras_model.save_weights(\"my_checkpoint\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "6kw5P4jt2Az8"
      },
      "source": [
        "モデルを作成するたびに新しいトレーニングループを作成する代わりに、Keras の組み込み機能をショートカットとして使用できます。これは、Python トレーニングループを作成またはデバッグしたくない場合に便利です。\n",
        "\n",
        "その場合は `model.compile()` を使用してパラメーターを設定し、`model.fit()` でトレーニングする必要があります。L2 損失と最急降下法の Keras 実装を再びショートカットとして使用するとコード量を少なくすることができます。Keras の損失とオプティマイザーはこれらの便利な関数の外でも使用できます。また、前の例ではこれらを使用できた可能性があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-nbLLfPE2pEl"
      },
      "outputs": [],
      "source": [
        "keras_model = MyModelKeras()\n",
        "\n",
        "# compile sets the training paramaeters\n",
        "keras_model.compile(\n",
        "    # By default, fit() uses tf.function().  You can\n",
        "    # turn that off for debugging, but it is on now.\n",
        "    run_eagerly=False,\n",
        "\n",
        "    # Using a built-in optimizer, configuring as an object\n",
        "    optimizer=tf.keras.optimizers.SGD(learning_rate=0.1),\n",
        "\n",
        "    # Keras comes with built-in MSE error\n",
        "    # However, you could use the loss function\n",
        "    # defined above\n",
        "    loss=tf.keras.losses.mean_squared_error,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lrlHODiZccu2"
      },
      "source": [
        "Keras `fit` は、バッチ処理されたデータまたは完全なデータセットを NumPy 配列として想定しています。NumPy 配列はバッチに分割され、デフォルトでバッチサイズは 32 になります。\n",
        "\n",
        "この場合は手書きループの動作に一致させるため、`x` をサイズ 1000 の単一バッチとして渡す必要があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "zfAYqtu136PO"
      },
      "outputs": [],
      "source": [
        "print(x.shape[0])\n",
        "keras_model.fit(x, y, epochs=10, batch_size=1000)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8zKZIO9P5s1G"
      },
      "source": [
        "Keras はトレーニング前ではなくトレーニング後に損失を出力するため、最初の損失は低く表示されますが、それ以外の場合は基本的に同じトレーニングパフォーマンスを示します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vPnIVuaSJwWz"
      },
      "source": [
        "## 次のステップ\n",
        "\n",
        "このガイドでは、テンソル、変数、モジュール、勾配テープの基本的なクラスを使用してモデルを構築およびトレーニングする方法と、それらの概念を Keras にマッピングする方法について説明しました。\n",
        "\n",
        "ただし、これはごく単純な問題です。より実践的な説明については、[カスタムトレーニングのウォークスルー](../tutorials/customization/custom_training_walkthrough.ipynb)をご覧ください。\n",
        "\n",
        "組み込みの Keras トレーニングループを使用する方法の詳細は、[こちらのガイド](keras/train_and_evaluate.ipynb)を参照してください。トレーニングループと Keras の詳細は、[こちらのガイド](keras/writing_a_training_loop_from_scratch.ipynb)を参照してください。独自の分散トレーニングループを書く方法については、[こちらのガイド](./guide/distributed_training.ipynb#using_tfdistributestrategy_with_basic_training_loops_loops)を参照してください。"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [
        "5rmpybwysXGV",
        "iKD__8kFCKNt",
        "vPnIVuaSJwWz"
      ],
      "name": "basic_training_loops.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
